//#include <strmif.h>
//#include <comdef.h>
#include <control.h>
#include <uuids.h>
#include "graphutil.hpp"
#include "mediatypeutil.hpp"
#include <cassert>


GraphUtil::IPinPtr GraphUtil::FindOutpin(IBaseFilter* f)
{
    return FindPin(f, PINDIR_OUTPUT);
}


GraphUtil::IPinPtr GraphUtil::FindInpin(IBaseFilter* f)
{
    return FindPin(f, PINDIR_INPUT);
}


GraphUtil::IPinPtr GraphUtil::FindOutpinVideo(IBaseFilter* f)
{
    return FindPin(f, PINDIR_OUTPUT, MEDIATYPE_Video);
}


GraphUtil::IPinPtr GraphUtil::FindOutpinAudio(IBaseFilter* f)
{
    return FindPin(f, PINDIR_OUTPUT, MEDIATYPE_Audio);
}


GraphUtil::IPinPtr GraphUtil::FindInpinVideo(IBaseFilter* f)
{
    return FindPin(f, PINDIR_INPUT, MEDIATYPE_Video);
}


GraphUtil::IPinPtr GraphUtil::FindInpinAudio(IBaseFilter* f)
{
    return FindPin(f, PINDIR_INPUT, MEDIATYPE_Audio);
}


ULONG GraphUtil::InpinCount(IBaseFilter* f)
{
    return PinCount(f, PINDIR_INPUT);
}


ULONG GraphUtil::OutpinCount(IBaseFilter* f)
{
    return PinCount(f, PINDIR_OUTPUT);
}


GraphUtil::IPinPtr
GraphUtil::FindPin(IBaseFilter* f, PIN_DIRECTION dir_requested)
{
    assert(f);
    
    IEnumPinsPtr e;
    
    HRESULT hr = f->EnumPins(&e);
    
    if (FAILED(hr))
        return 0;
        
    assert(bool(e));
    
    for (;;)
    {
        IPinPtr p;
        
        hr = e->Next(1, &p, 0);
        
        if (hr != S_OK)
            return 0;
            
        assert(bool(p));
        
        PIN_DIRECTION dir_actual;

        hr = p->QueryDirection(&dir_actual);

        if (SUCCEEDED(hr) && (dir_actual == dir_requested))
            return p;
    }
}


GraphUtil::IPinPtr
GraphUtil::FindPin(
    IBaseFilter* f, 
    PIN_DIRECTION dir_requested,
    const GUID& majortype)
{
    assert(f);
    
    IEnumPinsPtr e;
    
    HRESULT hr = f->EnumPins(&e);
    
    if (FAILED(hr))
        return 0;
        
    assert(bool(e));
    
    for (;;)
    {
        IPinPtr p;
        
        hr = e->Next(1, &p, 0);
        
        if (hr != S_OK)
            return 0;
            
        assert(bool(p));
        
        PIN_DIRECTION dir_actual;

        hr = p->QueryDirection(&dir_actual);

        if (FAILED(hr) || (dir_actual != dir_requested))
            continue;
            
        if (Match(p, majortype))
            return p;
    }
}


ULONG GraphUtil::PinCount(IBaseFilter* f)
{
    assert(f);
    
    IEnumPinsPtr e;
    
    HRESULT hr = f->EnumPins(&e);
    
    if (FAILED(hr))
        return 0;
        
    assert(bool(e));
    
    ULONG n = 0;
    
    for (;;)
    {
        IPinPtr p;
        
        hr = e->Next(1, &p, 0);
        
        if (hr != S_OK)
            return n;
            
        assert(bool(p));

        ++n;
    }
}


ULONG GraphUtil::PinCount(IBaseFilter* f, PIN_DIRECTION dir_requested)
{
    assert(f);
    
    IEnumPinsPtr e;
    
    HRESULT hr = f->EnumPins(&e);
    
    if (FAILED(hr))
        return 0;
        
    assert(bool(e));
    
    ULONG n = 0;
    
    for (;;)
    {
        IPinPtr p;
        
        hr = e->Next(1, &p, 0);
        
        if (hr != S_OK)
            return n;
            
        assert(bool(p));
        
        PIN_DIRECTION dir_actual;

        hr = p->QueryDirection(&dir_actual);

        if (SUCCEEDED(hr) && (dir_actual == dir_requested))        
            ++n;
    }
}


HRESULT GraphUtil::ConnectDirect(
    IFilterGraph* pGraph,
    IBaseFilter* fOut, 
    IBaseFilter* fIn,
    const AM_MEDIA_TYPE* pmt)
{
    assert(pGraph);
    assert(fOut);
    assert(fIn);
    
    const IPinPtr pOut(FindOutpin(fOut));
    
    if (!bool(pOut))
        return E_FAIL;
        
    const IPinPtr pIn(FindInpin(fIn));
    
    if (!bool(pIn))
        return E_FAIL;
        
    return pGraph->ConnectDirect(pOut, pIn, pmt);
}


bool GraphUtil::Match(IPin* pin, const GUID& majortype)
{
    assert(pin);
    
    IEnumMediaTypesPtr e;
    
    HRESULT hr = pin->EnumMediaTypes(&e);
    
    if (FAILED(hr))
        return false;
        
    assert(bool(e));
    
    for (;;)
    {
        AM_MEDIA_TYPE* pmt;
        
        hr = e->Next(1, &pmt, 0);
        
        if (hr != S_OK)
            return false;
            
        assert(pmt);
        
        const bool bMatch = (pmt->majortype == majortype) ? true : false;
        
        MediaTypeUtil::Free(pmt);
        
        if (bMatch)
            return true;
    }
}
